#include <algorithm>
#include <math.h>
#include <fstream>
#include <string>
#include <sstream>
#include <cassert>
#include "shader.h"
#include "scene.h"
#include "material.h"
using namespace std;
namespace gemren
{
/*coord2d shader::get_texture_coord(const intersection* i) const
{//interpolates texture coordinates from vertices
	//assert(i->triangle->datatype & 2);
	return	coord2d(0.0f,0.0f);
		    /*i->u * *i->triptr->t1 +
			i->v * *i->triptr->t2 +
			(1 - i->u - i->v) * *i->triptr->t0;*//*
}

void shader::get_abs_texcoord_base(const intersection* i, coord& right, coord& up)const
{//translates vectors (1,0) and (0,1) to world coordinates
/*	coord2d tv1 = *i->triptr->t1 - *i->triptr->t0;
	coord2d tv2 = *i->triptr->t2 - *i->triptr->t0;
	coord v1 = (*i->triptr->v2 - *i->triptr->v1),
		  v2 =	(*i->triptr->v3 - *i->triptr->v1);
	float_type a1,a2;
	//comp (1,0)
	float_type denom=0;
	if(abs(tv1.y)<EPSILON)
	{
		right = coord::normalize(tv1.x*v1); //watch the direction!
	}
	else
	{
		denom = 1/(tv2.x * tv1.y - tv1.x *tv2.y);
		float_type a2 = tv1.y * denom;
		float_type a1 = -a2*tv2.y/tv1.y;
		right = coord::normalize( a1 * v1 +	a2 * v2 );
	}
	//comp (0,1)
	if(abs(tv1.x)<EPSILON)
	{
		up = coord::normalize(tv1.y*v1); //watch the direction!
	}
	else
	{
		if(denom ==0) //if not set
			denom = 1/(tv2.x * tv1.y - tv1.x *tv2.y);
		a2 = -tv1.x * denom;
		a1 = -a2*tv2.x/tv1.x;
		up = coord::normalize( a1 * v1 +	a2 * v2 );
	}
	assert(abs( coord::dot(up,right) ) <EPSILON);
	assert(abs( coord::dot(up,i->normal) ) <EPSILON);
	assert(abs( coord::dot(i->normal,right) ) <EPSILON);
	*//*
}
shader_ptr shader::load_shader(const char filename[])
{
	return 0;/*
	ifstream f(filename);
	shader_combination* combo = new shader_combination;
	string line, atom;
	shader_ptr current = 0;
	while(!f.eof())
	{
		line = "";
		getline(f, line);
		if(line != "")
		{
			stringstream ls(line);
			atom = "";
			ls>>atom;
			if(atom == "shd")
			{
				//switch
				ls>>atom;
				if(atom == "phong_shading")
					current = new phong_shade;
				else if(atom == "simple_color")
					current = new simple_color_shader;
				else if(atom == "checkerboard")
					current = new checkerboard_shader;
				else if(atom == "phong_light")
					current = new phong_specular;
				else if(atom == "tiles")
					current = new tiles_shader;
				else if(atom == "bg_simple")
					current = new bg_simple_shader;

				combo->add_shader(current);
			}
			else if (atom == "par")
			{
				assert(current);
				bool valid_parameter = current->set_param(ls);
				assert(valid_parameter);
			}
		}
	}
    return combo;
}*/
/*
void simple_color_shader::shade(const Scene& scene,intersection* i) const
{
//	i->reflected_pc = 0; 
//	i->refracted_pc = 0; 
	float ints = 0;}
	//i->normal = i->triangle->normal();
	/*if( coord::dot(i->ray_dir, i->normal) > 0 ) //ray from inside
	{
		float_type absorbed = i->t * i->triangle->insidemat->absorption * i->triangle->insidemat->color[i->wavelength];
		if(absorbed>1) absorbed = 1;
		i->light_amount += absorbed;
		i->reflected_pc *= 1-absorbed;
		i->refracted_pc *= 1-absorbed;
		
		i->light_amount = 0;
	}
	else*//*
	if(i->reflected_pc+i->refracted_pc < 2-EPSILON)
	for(light_ptr l = scene.lbegin(); l!=scene.lend(); ++l)
	{
		//coord lightvec = coord::normalize( (*l)->get_light_pos() - i->abs_pos);
		ints += //(coord::dot(lightvec,i->normal)>0? (*l)->light_amount(i, 0):1) * 
			(*l)->get_intensity(i)*0.5* (1-coord::dot(i->normal, coord::normalize((*l)->get_light_vec(i))));
	}
	ints *= i->triangle->insidemat->color[i->wavelength];
	i->light_amount += ints;
	
//	const float_type okraj = 1e-2; 
//	if(i->u<okraj || i->v<okraj || 1- i->u - i->v <okraj) i->light_amount = 1; //draw edges - debug
}

void shader_combination::add_shader(shader* sh)
{
	shaders.push_back(sh);
}	
void shader_combination::shade(const RTscene& scene,intersection* i) const
{
	for(list<shader*>::const_iterator s = shaders.begin(); s!=shaders.end(); ++s)
		(*s)->shade(scene,i);
}
shader_combination::~shader_combination()
{
	for(list<shader*>::const_iterator s = shaders.begin(); s!=shaders.end(); ++s)
		delete (*s);
}

void phong_shade::shade(const RTscene& scene,intersection* i) const
{
	const triangle& t = *i->triangle;
	if(t.datatype & 4)
	{
		i->normal = (1- i->u - i->v) * *t.n1 + i->u * *t.n2 + i->v * *t.n3;  
	}
	else 
		i->normal = t.normal();
	//TODO put this elsewhere
	
}


void checkerboard_shader::shade(const RTscene&,intersection* i) const
{//TODO compute this default
	coord2d tx = i->u * *i->triangle->t2 +
				 i->v * *i->triangle->t3 +
				 (1 - i->u - i->v) * *i->triangle->t1;
	tx.x *= freq_x;
	tx.y *= freq_y;
	bool field = fmod(tx.x,1)<0.5 ? fmod(tx.y,1)<0.5 :fmod(tx.y,1)>0.5;
	if( field ) i->light_amount *= 0.2;
}
inline float_type sqr(float_type a)
{
	return a*a;
}
void tiles_shader::shade(const RTscene&,intersection* i) const
{//TODO compute this default
	coord2d tx = get_texture_coord(i);
	tx.x *= freq_x;
	tx.y *= freq_y;
	float_type pos = fmod(tx.x,1);
	coord rabs, uabs;
	get_abs_texcoord_base(i, rabs, uabs);
	
	float_type edge = fmod(tx.x,1);
	
	coord newnormal = i->normal;
	
	float_type x,dy;
		if(edge<t )
		{//left
			if(edge < EPSILON)
				newnormal = -rabs;
			else
			{
				x = 1/t*edge -1;
				dy = -x/sqrt(1-x*x);
				newnormal = coord::normalize(newnormal - dy*rabs);
			}
		}
		else if( edge> 1-t)
		{
			if(edge > 1-EPSILON)
				newnormal = rabs;
			else
			{
				x = 1/t *(edge-1)+1;
				dy = -x/sqrt(1-x*x);
				newnormal = coord::normalize(newnormal - dy*rabs);
			}
		}
		 edge =  fmod(tx.y,1);
		if(edge< t )
		{
			if(edge < EPSILON)
				newnormal = -uabs;
			else
			{
				x = 1/t*edge -1;
				dy = -x/sqrt(1-x*x);
				newnormal = coord::normalize(newnormal - dy*uabs);
			}
		}
		else if( edge > 1-t)
		{
			if(edge > 1-EPSILON)
				newnormal = uabs;
			else
			{
				x = 1/t *(edge-1)+1;
				dy = -x/sqrt(1-x*x);
				newnormal = coord::normalize(newnormal - dy*uabs);
			}
		}
		//newnormal = coord::normalize(newnormal);
		//assert(coord::dot(newnormal, newnormal)<1+EPSILON && coord::dot(newnormal, newnormal)>1-EPSILON );
	//}
	//else
		
		if(coord::dot(i->normal, newnormal) < .5)//??
			i->light_amount -=5;
		else 
			i->normal = newnormal;
		assert(abs(coord::dot(newnormal, newnormal)-1)<EPSILON);
}

bool checkerboard_shader::set_param(istream& is)
{
	string what;
	is>>what;
	if(what =="fx")
		is>>freq_x;
	else if(what =="fy")
		is>>freq_y;
	else return false;
	return true;
}

bool tiles_shader::set_param(istream& is)
{
	string what;
	is>>what;
	if(what =="fx")
		is>>freq_x;
	else if(what =="fy")
		is>>freq_y;
	else if(what =="thick")
		is>>t;
	else return false;
	return true;
}

void phong_specular::shade(const RTscene& scene,intersection* i) const
{//TODO compute this default
	if(coord::dot(i->normal, i->ray_dir)>0) return;
	
	for(light_ptr l = scene.lbegin(); l!=scene.lend(); ++l)
	{
		coord lightvec = coord::normalize((*l)->get_light_vec(i));
		float_type lcos = coord::dot(i->normal, lightvec);
		//if(lcos >0) continue;
		coord refl = lightvec - (2* lcos) * i->normal; //direction of light reflection
		lcos = coord::dot(i->ray_dir,refl);
		lcos *= lcos; 
		lcos *= lcos;
		
		i->light_amount += 
			(*l)->get_intensity(i)*lcos*ks;
	}
}

bool phong_specular::set_param(istream& is)
{
	string what;
	is>>what;
	if(what =="ks")
	{
		is>>ks;
		return true;
	}
	else return false;
}

void bg_simple_shader::shade(const RTscene& scene,intersection* i) const
{
	i->light_amount = intensity *(1+i->ray_dir.y);
}

bool bg_simple_shader::set_param(istream& is)
{
	string what;
	is>>what;
	if(what =="int")
	{
		is>>intensity;
		intensity *=0.5;
		return true;
	}
	else return false;
}*/

}